#![allow(dead_code)]

struct EnumLrn {}

impl EnumLrn {
    pub fn hello() {
        println!("Enum learning!");
    }
}

#[cfg(test)]
mod tests {
    #[test]
    #[allow(unused_variables)]
    fn three_methods() {
        // NOTE: c 风格枚举
        enum CStyleEnum {
            One,
            Two,
            Three,
        }

        // NOTE: 常规枚举
        enum NormalEnum {
            Beijing,
            Shanghai,
            Active(bool),
        }

        // NOTE: 复杂枚举
        #[derive(Debug, PartialEq)]
        enum ComplexEnum {
            Red,
            Active(bool),
            Complex { name: String, age: u8 },
        }

        // NOTE: enum 实例化
        let one = CStyleEnum::One;
        let location = NormalEnum::Beijing;
        let loc = NormalEnum::Active(true);

        let complex = ComplexEnum::Complex {
            name: String::from("hello"),
            age: 18,
        };

        // 选择enum
        fn match_enum(e: u8) -> ComplexEnum {
            match e {
                1 => ComplexEnum::Active(true),
                2 => ComplexEnum::Complex {
                    name: String::from("hello"),
                    age: 18,
                },
                _ => ComplexEnum::Red,
            }
        }

        fn match_enum_struct(e: ComplexEnum) -> String {
            match e {
                ComplexEnum::Complex { name, age } => format!("{} {}", name, age),
                _ => String::from(""),
            }
        }

        let e1 = match_enum(3);
        let e2 = match_enum(2);

        // println!("{:?}", e2);
        assert_eq!(e1, ComplexEnum::Red);
        assert_eq!(
            e2,
            ComplexEnum::Complex {
                name: String::from("hello"),
                age: 18,
            }
        );
        assert_eq!(match_enum_struct(e2), String::from("hello 18"));
    }

    #[test]
    /// NOTE: 测试多态
    /// std::fmt::Display 是 trait, derive 是自动实现#[derive()]宏
    /// enum ShapeEnum 集合了2个形态的结构体，实现了 Shape trait，enum的Shape主要是match。
    /// 然后实现了Shape for 每个结构体的方法，这样就可以通过enum的实例调用方法。
    fn polymorphism() {
        enum ShapeEnum {
            Rectangle(Rectangle),
            Circle(Circle),
        }

        #[derive(Debug)]
        struct Rectangle {
            width: f32,
            height: f32,
        }

        #[derive(Debug)]
        struct Circle {
            radius: f32,
        }

        trait Shape {
            fn perimeter(&self) -> f32;
            fn area(&self) -> f32;
        }

        impl Shape for ShapeEnum {
            fn perimeter(&self) -> f32 {
                match self {
                    ShapeEnum::Rectangle(r) => r.perimeter(),
                    ShapeEnum::Circle(c) => c.perimeter(),
                }
            }
            fn area(&self) -> f32 {
                match self {
                    ShapeEnum::Rectangle(r) => r.area(),
                    ShapeEnum::Circle(c) => c.area(),
                }
            }
        }

        impl std::fmt::Display for ShapeEnum {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                match self {
                    ShapeEnum::Rectangle(r) => write!(f, "{:?}", r),
                    ShapeEnum::Circle(c) => write!(f, "{:?}", c),
                }
            }
        }

        impl Shape for Rectangle {
            fn perimeter(&self) -> f32 {
                2.0 * (self.width + self.height)
            }
            fn area(&self) -> f32 {
                self.width * self.height
            }
        }

        impl Shape for Circle {
            fn perimeter(&self) -> f32 {
                2.0 * std::f32::consts::PI * self.radius
            }
            fn area(&self) -> f32 {
                std::f32::consts::PI * self.radius * self.radius
            }
        }

        fn check_shape(s: impl Shape + std::fmt::Display) {
            println!("{}'s perimeter: {}", s, s.perimeter());
            println!("{}'s area: {}", s, s.area());
        }

        // 同一类型
        let enum_rect = ShapeEnum::Rectangle(Rectangle {
            width: 10.0,
            height: 20.0,
        });
        let enum_circle = ShapeEnum::Circle(Circle { radius: 10.0 });

        // println!("enum_rect area: {}", enum_rect.area());
        // println!("enum_circle area: {}", enum_circle.area());
        check_shape(enum_rect);
        check_shape(enum_circle);
    }

    #[test]
    fn multi_return_type() {
        #[derive(Debug)]
        struct Unit(usize);

        #[derive(Debug)]
        struct Pair(usize, usize);

        #[derive(Debug)]
        enum ReturnType {
            U(Unit),
            P(Pair),
        }

        println!("return type: {:?}", return_type(2));

        fn return_type(x: usize) -> ReturnType {
            match x {
                1 => ReturnType::U(Unit(x)),
                2 => ReturnType::P(Pair(x, x)),
                _ => ReturnType::U(Unit(0)),
            }
        }
    }
}
